package models

import (
	"context"
	"database/sql/driver"
	"fmt"
	"go.uber.org/zap"
	"goadmin/config"
	"goadmin/logs"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
	gormlogger "gorm.io/gorm/logger"
	"os"
	"time"
)

var DB *gorm.DB
var zapLogger *zap.Logger

type JsonTime time.Time

const timeForm = "2006-01-02 15:04:05"

// 序列化：返回前端
func (t JsonTime) MarshalJSON() ([]byte, error) {
	b := make([]byte, 0, len(timeForm)+2)
	b = append(b, '"')
	b = time.Time(t).AppendFormat(b, timeForm)
	b = append(b, '"')
	return b, nil
}

// 反序列化：接收前端（可选）
func (t *JsonTime) UnmarshalJSON(data []byte) error {
	if len(data) < 2 || data[0] != '"' || data[len(data)-1] != '"' {
		return nil
	}
	tt, err := time.ParseInLocation(timeForm, string(data[1:len(data)-1]), time.Local)
	if err != nil {
		return err
	}
	*t = JsonTime(tt)
	return nil
}

// 数据库读写（GORM v2 自动兼容）
func (t JsonTime) Value() (driver.Value, error) {
	return time.Time(t), nil
}
func (t *JsonTime) Scan(v interface{}) error {
	if v == nil {
		return nil
	}
	switch vt := v.(type) {
	case time.Time:
		*t = JsonTime(vt)
	case string:
		tt, _ := time.ParseInLocation(timeForm, vt, time.Local)
		*t = JsonTime(tt)
	}
	return nil
}

func InitDB() {
	ip := config.Mysql.Ip
	port := config.Mysql.Port
	user := config.Mysql.User
	password := config.Mysql.Password
	database := config.Mysql.Database
	dsn := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8mb4&parseTime=True&loc=Local", user, password, ip, port, database)
	var err error
	DB, err = gorm.Open(mysql.Open(dsn), &gorm.Config{
		QueryFields: true, // 打印sql
		//Logger:      NewZapGormLogger(logs.Sugar()),
	})
	if err != nil {
		logs.Sugar().Errorf("failed to connect database: %v", err)
		os.Exit(1)
	}
	logs.Sugar().Info("database connected")
}

// NewZapGormLogger 适配 gorm logger 接口
func NewZapGormLogger(logger *zap.SugaredLogger) gormlogger.Interface {
	return &zapGormLogger{logger: logger}
}

type zapGormLogger struct {
	logger *zap.SugaredLogger
}

func (l *zapGormLogger) LogMode(level gormlogger.LogLevel) gormlogger.Interface {
	return l
}

func (l *zapGormLogger) Info(ctx context.Context, msg string, data ...interface{}) {
	l.logger.Infof(msg, data...)
}

func (l *zapGormLogger) Warn(ctx context.Context, msg string, data ...interface{}) {
	l.logger.Warnf(msg, data...)
}

func (l *zapGormLogger) Error(ctx context.Context, msg string, data ...interface{}) {
	l.logger.Errorf(msg, data...)
}
func (l *zapGormLogger) Trace(ctx context.Context, begin time.Time, fc func() (string, int64), err error) {
	elapsed := time.Since(begin)
	sql, rows := fc()

	fields := []zap.Field{
		zap.String("sql", sql),
		zap.Int64("rows", rows),
		zap.Duration("elapsed", elapsed),
	}
	if err != nil {
		fields = append(fields, zap.Error(err))
		logs.Sugar().Error("SQL执行错误", fields)
	} else if elapsed > time.Second {
		fields = append(fields, zap.String("level", "slow"))
		logs.Sugar().Warn("慢SQL查询", fields)
	} else {
		logs.Sugar().Info("SQL执行", fields)
	}
}
